#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <curses.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <deque>
#include <string>
#include <thread>
#include <mutex>
#include <atomic>

#include "utils.h"

typedef struct
{
    int w;
    int h;
    int x;
    int y;
} WIN;

// 创建窗口
WINDOW *create_newwin(WIN &win)
{
    WINDOW *local_win;
    local_win = newwin(win.h, win.w, win.y, win.x);
    box(local_win, 0, 0);
    wrefresh(local_win);
    return local_win;
}

const char *header_str()
{
    return ""
           "tchat Verion 0.1.0";
}

// 队列锁, 用于保存发送的和收到的消息
std::mutex g_dq_mutex;
// 窗口锁, 用于多线程操作窗口
std::mutex g_win_mutex;
// 退出标志位, 可以不需要锁
static bool g_exit;
// 保存
static std::atomic<int> g_get_sock;

// 参数位置
static int IDX = 0;
#ifdef CUSTOM_PORT
const static int LOCAL_IP_IDX = ++IDX;
const static int LOCAL_PORT_IDX = ++IDX;
const static int REMOTE_IP_IDX = ++IDX;
const static int REMOTE_PORT_IDX = ++IDX;
#else
const static int LOCAL_IP_IDX = ++IDX;
const static int REMOTE_IP_IDX = ++IDX;
const static char *LOCK_PORT = "17878";
#endif

static char LOCAL_IP[32];
static char LOCAL_PORT[32];
static char REMOTE_IP[32];
static char REMOTE_PORT[32];
int main(int argc, char **argv)
{
    // curses初始化
    initscr();
    cbreak();
    refresh();

    // ip 和 port 参数
    memcpy(LOCAL_IP, argv[LOCAL_IP_IDX], strlen(argv[LOCAL_IP_IDX]));
    memcpy(REMOTE_IP, argv[REMOTE_IP_IDX], strlen(argv[REMOTE_IP_IDX]));
#ifdef CUSTOM_PORT
    memcpy(LOCAL_PORT, argv[LOCAL_PORT_IDX], strlen(argv[LOCAL_PORT_IDX]));
    memcpy(REMOTE_PORT, argv[REMOTE_PORT_IDX], strlen(argv[REMOTE_PORT_IDX]));
#else
    if (argc == 4)
    {
        memcpy(LOCAL_PORT, argv[3], strlen(argv[3]));
        memcpy(REMOTE_PORT, argv[3], strlen(argv[3]));
    }
    else
    {
        memcpy(LOCAL_PORT, LOCK_PORT, strlen(LOCK_PORT));
        memcpy(REMOTE_PORT, LOCK_PORT, strlen(LOCK_PORT));
    }
#endif

    // 窗口布局
    WIN header = {COLS - 2, 3, 1, 0};
    WIN msg = {COLS - 2, LINES - 6, 1, 3};
    WIN edit = {COLS - 2, 3, 1, LINES - 3};

    // 窗口创建
    WINDOW *header_win = create_newwin(header);
    WINDOW *msg_win = create_newwin(msg);
    WINDOW *edit_win = create_newwin(edit);

    // 消息容器
    std::deque<std::string> msg_dq;
    int max_dq = msg.h - 2;
    g_exit = false;

    keypad(stdscr, TRUE);
    wmove(edit_win, 1, 1);

    // 头部打印, 用于显示一些状态消息
    auto print_header = [&header_win](auto &str)
    {
        const std::lock_guard<std::mutex> lock(g_win_mutex);
        wclear(header_win);
        box(header_win, 0, 0);
        mvwprintw(header_win, 1, 1, "%s", str);
        wrefresh(header_win);
    };

    // 消息窗口打印, 用于打印双方的消息
    auto print_msg = [&msg_dq, &msg_win, &edit_win]()
    {
        wclear(msg_win);
        box(msg_win, 0, 0);
        int idx = 1;
        for (auto &s : msg_dq)
        {
            mvwprintw(msg_win, idx++, 1, "%s", s.c_str());
        }
        wrefresh(msg_win);
    };

    // 消息塞入消息容器
    auto push_msg = [&max_dq, &msg_dq, &print_msg](auto &msg)
    {
        const std::lock_guard<std::mutex> lock(g_dq_mutex);
        msg_dq.push_back(msg);
        while (msg_dq.size() >= max_dq)
        {
            msg_dq.pop_front();
        }
        print_msg();
    };

    // 发送消息, 单独线程
    auto put_msg = [&argv, &push_msg, &print_header, &edit_win]()
    {
        bool init = true;
        int sock = 0;
        print_header("connecting...");
        while (!g_exit)
        {
            // 尝试链接
            sock = connect_socket(REMOTE_IP, atoi(REMOTE_PORT));
            // 如果链接失败则继续尝试连接
            if (!test_socket(sock))
            {
                // 如果连接成功后又断开了连接, 则退出
                if (!init)
                    break;
                sleep(1);
                continue;
            }
            if (init)
            {
                print_header("connected.");
            }
            init = false;

            char msg[1024];
            memset(msg, 0, sizeof(msg));
            // 阻塞的
            wgetstr(edit_win, msg);
            write(sock, msg, sizeof(msg));

            wclear(edit_win);
            box(edit_win, 0, 0);
            wmove(edit_win, 1, 1);

            char nmsg[1024];
            sprintf(nmsg, "say: %s", msg);
            push_msg(nmsg);
        }
        close(sock);

        print_header("remote disconnected.");
        sleep(1);
        g_exit = true;
    };

    // 获取消息, 单独线程
    auto get_msg = [&argv, &push_msg, &print_header]()
    {
        int sock = bind_socket(LOCAL_IP, atoi(LOCAL_PORT));
        g_get_sock = sock;
        listen(sock, 1024);
        struct sockaddr_in remote_addr;
        socklen_t remote_addr_size = sizeof(remote_addr);
        while (test_socket(sock) && !g_exit)
        {
            // 阻塞的
            int remote = accept(sock, (struct sockaddr *)&remote_addr, &remote_addr_size);
            if (0 == remote)
                break;

            char msg[1024];
            memset(msg, 0, sizeof(msg));
            read(remote, msg, sizeof(msg));
            close(remote);

            char nmsg[1024];
            sprintf(nmsg, "%s say: %s", inet_ntoa(remote_addr.sin_addr), msg);
            push_msg(nmsg);
        }
        close(sock);

        print_header("local disconnected.");
        sleep(1);
        g_exit = true;
    };

    auto thread_put = std::thread(put_msg);
    auto thread_get = std::thread(get_msg);
    thread_put.join();
//     // TODO accept 阻塞问题
//     goto EXIT;

// EXIT:
    // TODO accept 阻塞问题
    thread_get.detach();
    pthread_cancel(thread_get.native_handle());
    close(g_get_sock);

    wclear(header_win);
    wclear(msg_win);
    wclear(edit_win);
    delwin(header_win);
    delwin(msg_win);
    delwin(edit_win);
    endwin();
    exit(0);
}